#include "StdAfx.h"
#include "ZipFile.h"
#include "MiscFunc.h"
#include "../Zip/zlib.h"

extern "C"
{
#include "../LZMA/LzmaUtil/Lzma86Enc.h"
#include "../LZMA/LzmaUtil/Lzma86Dec.h"
}

#define ZIP_SIG 'ZTK'

typedef struct tagZipFileHeader
{
	DWORD dwSig;
	DWORD dwVer;
	DWORD dwLenPreCompress;
	DWORD dwLenPostCompress;

	tagZipFileHeader()
	{
		memset(this, 0, sizeof(*this));

		dwSig = ZIP_SIG;
		dwVer = 1;
	}
}ZIP_FILE_HEADER, *LPZIP_FILE_HEADER;

int ZFEncode(Byte *pDest, DWORD *pdwDestLen, const Byte *pSrc, DWORD dwSrcLen, int nEncMethod)
{
	int nRet = -1;

	if (nEncMethod == ENC_ZIP)
	{
		nRet = compress2(pDest, pdwDestLen, pSrc, dwSrcLen, Z_BEST_COMPRESSION);
	}
	else if (nEncMethod == ENC_LZMA)
	{
		nRet = Lzma86_Encode(pDest, (size_t *)pdwDestLen, pSrc, dwSrcLen, 5, 1 << 23, SZ_FILTER_YES);
	}

	return nRet;
}

int ZFDecode(Byte *pDest, DWORD *pdwDestLen, const Byte *pSrc, DWORD *pdwSrcLen)
{
	int nRet = -1;

	nRet = uncompress(pDest, pdwDestLen, pSrc, *pdwSrcLen);
	if (nRet != 0)
	{
		nRet = Lzma86_Decode(pDest, (size_t *)pdwDestLen, pSrc, (size_t *)pdwSrcLen);
	}

	return nRet;
}

BOOL CompressFile(const char *szSrcFile, const char *szDstFile, int nEncMethod)
{
	BOOL bRet = FALSE;

	if (nEncMethod != ENC_ZIP && nEncMethod != ENC_LZMA)
	{
		nEncMethod = ENC_ZIP;
	}

	char szTemp[_MAX_PATH] = { 0 };

	CFile file;
	CFile fileDst;
	if (file.Open(szSrcFile, CFile::modeRead))
	{
		lstrcpy(szTemp, szDstFile);
		miscGetFilePath(szTemp);
		miscCreateFullDirectory(szTemp);

		DWORD dwHeaderLen = sizeof(ZIP_FILE_HEADER);
		DWORD dwFileLen = (DWORD)file.GetLength();
		DWORD dwZipBufLen = dwHeaderLen + dwFileLen + (DWORD)(dwFileLen * 0.001 + 0.5) + 12;
		if (nEncMethod == ENC_LZMA)
		{
			dwZipBufLen = dwHeaderLen + dwFileLen / 20 * 21 + (1 << 16);
		}

		BYTE *pSrcBuff = (BYTE *)malloc(dwFileLen);
		BYTE *pDstBuff = (BYTE *)malloc(dwZipBufLen);

		if (pSrcBuff && pDstBuff)
		{
			if (file.Read(pSrcBuff, dwFileLen) == dwFileLen)
			{
				file.Close();

				//if (compress2(pDstBuff + dwHeaderLen, &dwZipBufLen, pSrcBuff, dwFileLen, Z_BEST_COMPRESSION) == Z_OK)
				if (ZFEncode(pDstBuff + dwHeaderLen, &dwZipBufLen, pSrcBuff, dwFileLen, nEncMethod) == 0)
				{
					ZIP_FILE_HEADER *pHeader = (ZIP_FILE_HEADER *)pDstBuff;
					pHeader->dwSig = ZIP_SIG;
					pHeader->dwVer = 1;
					pHeader->dwLenPreCompress = dwFileLen;
					pHeader->dwLenPostCompress = dwZipBufLen;

					BOOL bDeleted = TRUE;
					if (miscFileExist(szDstFile))
					{
						bDeleted = DeleteFile(szDstFile);
					}
					if (bDeleted)
					{
						if (fileDst.Open(szDstFile, CFile::modeCreate | CFile::modeWrite))
						{
							fileDst.Write(pDstBuff, dwZipBufLen + dwHeaderLen);
							fileDst.Close();

							bRet = TRUE;
						}
					}
				}
			}
		}

		free(pSrcBuff);
		free(pDstBuff);
	}

	return bRet;
}

BOOL CopyTo(const char *szSrcFile, const char *szDstFile, BOOL bFailIfExist)
{
	char szTemp[_MAX_PATH] = { 0 };

	if (lstrcmpi(szSrcFile, szDstFile) == 0)
	{
		return TRUE;
	}

	lstrcpy(szTemp, szDstFile);
	miscGetFilePath(szTemp);
	miscCreateFullDirectory(szTemp);

	return CopyFile(szSrcFile, szDstFile, bFailIfExist);
}

BOOL UnCompressFile(const char *szSrcFile, const char *szDstFile)
{
	BOOL bRet = FALSE;

	char szTemp[_MAX_PATH] = { 0 };

	CFile file;
	CFile fileDst;
	if (file.Open(szSrcFile, CFile::modeRead))
	{
		DWORD dwHeaderLen = sizeof(ZIP_FILE_HEADER);
		DWORD dwFileLen = (DWORD)file.GetLength();

		if (dwFileLen > dwHeaderLen)
		{
			BYTE *pSrcBuff = (BYTE *)malloc(dwFileLen);
			BYTE *pDstBuff = NULL;

			if (pSrcBuff && (file.Read(pSrcBuff, dwFileLen) == dwFileLen))
			{
				file.Close();

				ZIP_FILE_HEADER *pHeader = (ZIP_FILE_HEADER *)pSrcBuff;
				DWORD dwDstBufLen = pHeader->dwLenPreCompress;
				DWORD dwZipDataLen = pHeader->dwLenPostCompress;

				if (pHeader->dwSig == ZIP_SIG)
				{
					if (dwZipDataLen == (dwFileLen - dwHeaderLen))
					{
						pDstBuff = (BYTE *)malloc(dwDstBufLen);
						if (pDstBuff)
						{
							//if (uncompress(pDstBuff, &dwDstBufLen, pSrcBuff + dwHeaderLen, dwZipDataLen) == Z_OK)
							if (ZFDecode(pDstBuff, &dwDstBufLen, pSrcBuff + dwHeaderLen, &dwZipDataLen) == 0)
							{
								BOOL bDeleted = TRUE;
								if (miscFileExist(szDstFile))
								{
									bDeleted = DeleteFile(szDstFile);
								}
								if (bDeleted)
								{
									if (fileDst.Open(szDstFile, CFile::modeCreate | CFile::modeWrite))
									{
										fileDst.Write(pDstBuff, dwDstBufLen);
										fileDst.Close();

										bRet = TRUE;
									}
								}
							}
						}
					}
				}
				else
				{
					bRet = CopyTo(szSrcFile, szDstFile, FALSE);
				}
			}

			free(pSrcBuff);
			free(pDstBuff);
		}
		else
		{
			bRet = CopyTo(szSrcFile, szDstFile, FALSE);
		}
	}

	return bRet;
}
